package com.ruoyi.vote.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.vote.VoteException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.mall.enums.OrderInfoEnum;
import com.ruoyi.vote.admin.domain.vo.VoteActivityVo;
import com.ruoyi.vote.admin.domain.vo.VoteCertificateVo;
import com.ruoyi.vote.admin.service.IVoteActivityService;
import com.ruoyi.vote.admin.service.IVoteCertificateReceiveService;
import com.ruoyi.vote.admin.service.IVoteExamineService;
import com.ruoyi.vote.common.enums.CertificateTypeEnum;
import com.ruoyi.vote.common.constants.ModelTypeConstants;
import com.ruoyi.vote.common.constants.PayConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.ruoyi.vote.admin.domain.bo.VoteCertificateReceiveBo;
import com.ruoyi.vote.admin.domain.vo.VoteCertificateReceiveVo;
import com.ruoyi.vote.admin.domain.VoteCertificateReceive;
import com.ruoyi.vote.admin.mapper.VoteCertificateReceiveMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;

/**
 * 活动 - 证书申请Service业务层处理
 *
 * @author 筑巢
 * @date 2023-07-17
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class VoteCertificateReceiveServiceImpl extends ServiceImpl<VoteCertificateReceiveMapper, VoteCertificateReceive> implements IVoteCertificateReceiveService {

    private final VoteCertificateReceiveMapper baseMapper;
    private final VoteCertificateServiceImpl certificateService;
    private final IVoteExamineService examineService;
    private final IVoteActivityService activityService;

    /**
     * 查询活动 - 证书申请
     */
    @Override
    public VoteCertificateReceiveVo queryById(Long id){
        VoteCertificateReceiveVo voteCertificateReceiveVo = baseMapper.selectVoById(id);
        voteCertificateReceiveVo.setVoteExamines(examineService.getExamineList(ModelTypeConstants.CERTIFICATE,id));
        return voteCertificateReceiveVo;
    }

    /**
     * 查询活动 - 证书申请列表
     */
    @Override
    public TableDataInfo<VoteCertificateReceiveVo> queryPageList(VoteCertificateReceiveBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<VoteCertificateReceive> lqw = buildQueryWrapper(bo);
        Page<VoteCertificateReceiveVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        result.setRecords(result.getRecords().stream().peek(receive -> receive.setCertificateVo(certificateService.queryById(receive.getCerId()))).collect(Collectors.toList()));
        return TableDataInfo.build(result);
    }

    /**
     * 查询证书申请列表
     */
    @Override
    public List<VoteCertificateReceiveVo> queryList(VoteCertificateReceiveBo bo) {
        LambdaQueryWrapper<VoteCertificateReceive> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<VoteCertificateReceive> buildQueryWrapper(VoteCertificateReceiveBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<VoteCertificateReceive> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getPrice() != null, VoteCertificateReceive::getPrice, bo.getPrice());
        lqw.eq(bo.getStatus() != null, VoteCertificateReceive::getStatus, bo.getStatus());
        if (UserType.XCX_MEMBER.equals(LoginHelper.getUserType())){
            lqw.eq(VoteCertificateReceive::getMId,LoginHelper.getUserId());
        }
        lqw.orderByDesc(VoteCertificateReceive::getCreateTime);
        return lqw;
    }

    /**
     * 新增证书申请
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(VoteCertificateReceiveBo bo) {
        VoteCertificateVo voteCertificateVo = certificateService.queryById(bo.getCerId());
        if (voteCertificateVo == null) {
            throw new RuntimeException("证书模板不存在");
        }

        VoteActivityVo activityVo = activityService.queryById(bo.getAId());
        if (activityVo == null) {
            throw new VoteException("vote.activity.doesNotExist");
        }
        bo.setATitle(activityVo.getTitle());

        Long userId = LoginHelper.getUserId();
        int cType = bo.getCType();   // 1
        // 电子
        boolean electronicReceived = isCertificateReceived(bo.getAId(), bo.getMId(), CertificateTypeEnum.ELECTRONIC);
        // 纸质
        boolean paperReceived = isCertificateReceived(bo.getAId(), userId, CertificateTypeEnum.PAPER);

        // 针对小程序会员设置领取者ID和检查是否已领取电子证书/纸质证书
        if (UserType.XCX_MEMBER.equals(LoginHelper.getUserType())) {
            bo.setMId(userId);
            bo.setPhone(LoginHelper.getLoginUser().getPhone());
            if (cType == CertificateTypeEnum.PAPER.getCode() && paperReceived) {
                throw new RuntimeException("已经领取过纸质证书，无法重复领取");
            }
            if (bo.getCType() == CertificateTypeEnum.ELECTRONIC.getCode() && electronicReceived){
                throw new RuntimeException("已经领取过电子证书，无法继续领取");
            }
        }

        if (cType == CertificateTypeEnum.PAPER.getCode() && !paperReceived){
            VoteCertificateReceive add = BeanUtil.toBean(bo, VoteCertificateReceive.class);
            add.setIsPay(PayConstants.ERROR);
            add.setStatus(OrderInfoEnum.STATUS_0.getValue());
            log.info("--- 证书申领 - 纸质证书申领, 用户: {}, 数据: {}", userId, add);
            baseMapper.insert(add);
        }

        createElectronicCertificate(bo, voteCertificateVo);
        return true;
    }

    /**
     * 修改证书申请
     */
    @Override
    public Boolean updateByBo(VoteCertificateReceiveBo bo) {
        if (!bo.getStatus().equals(OrderInfoEnum.STATUS_0.getValue())) return false;
        VoteCertificateReceive update = BeanUtil.toBean(bo, VoteCertificateReceive.class);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 批量删除证书申请
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 创建电子档
     * @param bo
     * @param voteCertificateVo
     */
    private void createElectronicCertificate(VoteCertificateReceiveBo bo, VoteCertificateVo voteCertificateVo) {
        // 电子
        boolean electronicReceived = isCertificateReceived(bo.getAId(), bo.getMId(), CertificateTypeEnum.ELECTRONIC);
        if (bo.getCType() == CertificateTypeEnum.ELECTRONIC.getCode() && electronicReceived){
            throw new RuntimeException("已经领取过电子证书，无法继续领取");
        }
        if(!electronicReceived){
            VoteCertificateReceive electronicCertificate = BeanUtil.toBean(bo, VoteCertificateReceive.class);
            electronicCertificate.setCType(CertificateTypeEnum.ELECTRONIC.getCode());
            electronicCertificate.setIsPay(PayConstants.SUCCESS);
            electronicCertificate.setStatus(OrderInfoEnum.STATUS_3.getValue());
            log.info("--- 证书申领 - 电子证书申领, 用户: {}, 数据: {}", bo.getMId(), electronicCertificate);
            baseMapper.insert(electronicCertificate);
        }
    }

    /**
     * 查询是否已领取过指定类型的证书
     * @param aId
     * @param userId
     * @param certificateType
     * @return  true已领取
     */
    private Boolean isCertificateReceived(Long aId, Long userId, CertificateTypeEnum certificateType) {
        List<VoteCertificateReceive> receive = baseMapper.selectList(new LambdaQueryWrapper<VoteCertificateReceive>()
            .eq(VoteCertificateReceive::getAId, aId)
            .eq(VoteCertificateReceive::getMId, userId)
            .eq(VoteCertificateReceive::getIsPay,PayConstants.SUCCESS)
            .ge(VoteCertificateReceive::getStatus,OrderInfoEnum.STATUS_1)
            .eq(VoteCertificateReceive::getCType, certificateType.getCode()));
        return CollUtil.isNotEmpty(receive);
    }
}
